﻿// Take_a_screenshot.cpp : 定义应用程序的入口点。
//


/*
注意注意注意注意！！！！！！！！！！
    该项目的属性在没有任何设置的时候（除了多字节字符集和SDL检查外），会出现：
        错误(活动)	E0167	"const char *" 类型的实参与 "char *" 类型的形参不兼容	


    该问题的解决流程是:
        项目属性-> c/c++ -> 语言 -> 符号模式 -> 由是改为否！！！！！！！
        

    所以前面文件部分应该也是这个问题            妈的 文件那里不是这个问题  是tm我把函数写错了 我tm真sb




*/





#include "pch.h"
#include "framework.h"
#include "Take_a_screenshot.h"

#define MAX_LOADSTRING 100


#include <stdio.h>
#include <wingdi.h>
#pragma comment(lib,"Msimg32.lib")


// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名




//全局变量
    //要显示的全屏dc：
HDC     screenDc;
    //桌面宽 高：
int     screenWidth, screenHeight;
    //鼠标左键前后坐标：
POINT   pDown, pUp;
    //桌面dc：
HDC displayDc;
    //标记左键按下：
bool isDone;


/*
截屏：
    1.获取当前桌面屏幕
        1.1获取桌面
            HDC
        1.2获取桌面宽高

        1.3显示

    2.获取到要截屏的部分
        2.1要获取鼠标左键按下 和鼠标左键弹起的坐标

        2.2注意 矩形区域 不是一定是左键按下是左上角 左键弹起是右下角

            按下 弹起
            左下 右上
            右下 左上
            右上 左下

            只看大小    小的是左上角  大的是右下角

        2.3只能相应当前窗口上的鼠标消息
            2.3.1   窗口还没有显示的时候湖区到屏幕
            2.3.2   窗口显示的时候 显示屏幕 覆盖桌面
            2.3.3   截完屏 窗口退出


    3.存储
        3.1存放到剪切板中

        3.2存储成图片文件

*/


// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


//函数声明：
void OnCreate(HWND hWnd);
    //获取到要截取的部分 并显示或者保存
void saveimage(HWND hWnd);

void draw(HWND hWnd); 
//把hbitmap转存到文件中
bool SaveBitmapToFile(HBITMAP hBitmap, char* filename);

//计算位图文件每个像素所占字节数
WORD GetBitmapBitCount();

//处理调色板
void ProcessPalette(HBITMAP hBitmap, const BITMAP& bitmap,
    DWORD paletteSize, LPBITMAPINFOHEADER lpBmpInfoHeader);





int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_TAKEASCREENSHOT, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TAKEASCREENSHOT));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TAKEASCREENSHOT));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = NULL; /*干掉菜单栏*/  // MAKEINTRESOURCEW(IDC_TAKEASCREENSHOT);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle,
      WS_POPUPWINDOW,   //弹出式窗口
      0,0,GetSystemMetrics(SM_CXFULLSCREEN),GetSystemMetrics(SM_CYFULLSCREEN),
      nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_MOUSEMOVE:
        if (isDone)
        {
            draw(hWnd);
        }
        break;
    case WM_LBUTTONDOWN:
    {
        //左键按下：

        // x = LOWORD(lParam)
        // y = HIWORD(lParam)

        GetCursorPos(&pDown);
        isDone = true;
        break;
    }
        
    case WM_LBUTTONUP:
    {
        //左键弹起：
        GetCursorPos(&pUp);

#if 0
        //test:
        char buff[256];
        sprintf(buff, "(x1:%d,y1:%d)-----(x2:%d,y2:%d)",
            pDown.x, pDown.y, pUp.x, pUp.y);        //显示的是桌面的坐标 而不是窗口的坐标
        MessageBox(NULL, buff, "Rectangular area coordinates：", NULL);
#endif

        //保存图片：
        saveimage(hWnd);


        isDone = false;

        //截完屏 窗口退出：
        PostQuitMessage(0);
        break;
    }
        
    case WM_CREATE:
        OnCreate(hWnd);
        break;




    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...




#if 1
            //获取当前窗口宽 高：
            RECT rect = { 0 };
            GetWindowRect(hWnd, &rect);

            //等比例缩放：
            TransparentBlt(hdc, 0, 0,
                rect.right - rect.left, rect.bottom - rect.top,
                screenDc, 0, 0, screenWidth, screenHeight, SRCCOPY);
          

        
#else
            OnCreate(hWnd);

#endif





            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}



//函数定义：
void draw(HWND hWnd)
{
#if 0
    //1.获取当前的桌面屏幕：

    //1.1获取桌面
    displayDc = CreateDC("DISPLAY"/*获取桌面，这个函数的本质是获取DC，加上这个就是获取桌面*/,
        NULL, NULL, NULL);

    //1.2获取桌面宽 高 
    //GetSystemMetrics      //检索指定的系统指标或系统配置设置。
    screenWidth  = GetSystemMetrics(SM_CXFULLSCREEN);     //GetDeviceCaps(displayDc, HORZRES);    //全局变量
    screenHeight = GetSystemMetrics(SM_CYFULLSCREEN);     //GetDeviceCaps(displayDc, VERTRES);   //全局变量

#if 0
    //test:
    char buff[256];
    sprintf(buff, "hdc:%x    width:%d    height:%d", displayDc, screenWidth, screenHeight);
    MessageBox(NULL, buff, "tips", NULL);
#endif

    //1.3绘制到兼容dc中：

    //创建兼容dc：(全局变量）
    screenDc = CreateCompatibleDC(displayDc);
    //创建兼容位图：
    HBITMAP bitmap = CreateCompatibleBitmap(displayDc, screenWidth, screenHeight);
    //选进去：
    SelectObject(screenDc, bitmap);
    //画：
    BitBlt(screenDc, 0, 0, screenWidth, screenHeight, displayDc, 0, 0, SRCCOPY);

#endif

    //1.4显示到当前窗口中

    //获取当前窗口dc：
    HDC hdc = GetDC(hWnd);
     
    //获取当前窗口宽 高：
    RECT rect = { 0 };
    GetWindowRect(hWnd, &rect);

    //等比例缩放：
    TransparentBlt(hdc, 0, 0,
        screenWidth,screenHeight,
        screenDc, 0, 0, screenWidth, screenHeight, SRCCOPY);

    HPEN hPen = CreatePen(BS_SOLID, 3, RGB(255, 0, 0));

    HGDIOBJ hOldPen = SelectObject(hdc, hPen);


    POINT p;
    GetCursorPos(&p);



    //Rectangle(hdc, pDown.x, pDown.y, p.x, p.y);
    
    MoveToEx(hdc, pDown.x, pDown.y,NULL);
    LineTo(hdc, p.x, pDown.y);
    LineTo(hdc, p.x, p.y);
    LineTo(hdc, pDown.x, p.y);
    LineTo(hdc, pDown.x, pDown.y);

    
    SelectObject(hdc, hOldPen);


    ReleaseDC(hWnd, hdc);
}



void OnCreate(HWND hWnd)
{
    //1.获取当前的桌面屏幕：

    //1.1获取桌面：
    displayDc = CreateDC("DISPLAY"/*获取桌面，这个函数的本质是获取DC，加上这个就是获取桌面*/,
        NULL, NULL, NULL);

    //1.2获取桌面宽 高 
    //GetSystemMetrics      //检索指定的系统指标或系统配置设置。
    screenWidth = GetDeviceCaps(displayDc, HORZRES);    //全局变量
    screenHeight = GetDeviceCaps(displayDc, VERTRES);   //全局变量

#if 0
    //test:
    char buff[256];
    sprintf(buff, "hdc:%x    width:%d    height:%d", displayDc, screenWidth, screenHeight);
    MessageBox(NULL, buff, "tips", NULL);
#endif

    //1.3绘制到兼容dc中：

    //创建兼容dc：(全局变量）
    screenDc = CreateCompatibleDC(displayDc);
    //创建兼容位图：
    HBITMAP bitmap =  CreateCompatibleBitmap(displayDc, screenWidth, screenHeight);
    //选进去：
    SelectObject(screenDc, bitmap);
    //画：
    BitBlt(screenDc, 0, 0, screenWidth, screenHeight, displayDc, 0, 0, SRCCOPY);

#if 1
    //1.4显示到当前窗口中
    
    //获取当前窗口dc：
    HDC hdc = GetDC(hWnd);

    //获取当前窗口宽 高：
    RECT rect = { 0 };
    GetWindowRect(hWnd, &rect);

    //等比例缩放：
    TransparentBlt(hdc, 0, 0,
        rect.right - rect.left, rect.bottom - rect.top,
        screenDc, 0, 0, screenWidth, screenHeight, SRCCOPY);



    //释放当前窗口dc：
    ReleaseDC(hWnd, hdc);
    isDone = false;

#endif

}

//获取要截取的部分 并 显示 或者保存：
void saveimage(HWND hWnd)
{
    //获取要截取的部分的宽高：
    //pUp是右下角  pDown是左上角
    int saveWidth  = pUp.x - pDown.x;
    int saveHeight = pUp.y - pDown.y;


    //创建局部兼容dc：
    HDC saveDc = CreateCompatibleDC(displayDc);
    //创建兼容位图：
    HBITMAP savemap = CreateCompatibleBitmap(displayDc,
        saveWidth, saveHeight);
    //选进去：
    SelectObject(saveDc, savemap);
    //画：
    BitBlt(saveDc, 0, 0, saveWidth, saveHeight, displayDc,
        pDown.x, pDown.y,//起始坐标
        SRCCOPY);


    //存储到剪切板里：

    //打开剪切板：
    OpenClipboard(NULL);
    EmptyClipboard();   //清空剪切板
    //把位图放到剪切板中：
    SetClipboardData(CF_BITMAP, savemap);

    //关闭剪切板：
    CloseClipboard();

    
    SaveBitmapToFile(savemap, "jietu.bmp");
}

//把hbitmap转存到文件中
bool SaveBitmapToFile(HBITMAP hBitmap, char* filename)
{
    //1. 创建位图文件
    HANDLE file = CreateFile(filename, GENERIC_WRITE,
        0, nullptr, CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
        nullptr);
    if (file == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    //2. 计算位图文件每个像素所占字节数
    WORD bitCount = GetBitmapBitCount();

    //3. 获取位图结构
    BITMAP bitmap;
    ::GetObject(hBitmap, sizeof(bitmap), reinterpret_cast<LPSTR>(&bitmap));

    //位图中像素字节大小(32字节对齐)
    const DWORD bmBitsSize = ((bitmap.bmWidth * bitCount + 31) / 32) * 4 * bitmap.bmHeight;

    //调色板大小
    const DWORD paletteSize = 0;

    //4. 构造位图信息头
    BITMAPINFOHEADER  bmpInfoHeader; //位图信息头结构
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfoHeader.biWidth = bitmap.bmWidth;
    bmpInfoHeader.biHeight = bitmap.bmHeight;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = bitCount;
    bmpInfoHeader.biCompression = BI_RGB;
    bmpInfoHeader.biSizeImage = 0;
    bmpInfoHeader.biXPelsPerMeter = 0;
    bmpInfoHeader.biYPelsPerMeter = 0;
    bmpInfoHeader.biClrImportant = 0;
    bmpInfoHeader.biClrUsed = 0;

    //5. 构造位图文件头
    BITMAPFILEHEADER bmpFileHeader;
    bmpFileHeader.bfType = 0x4D42; //"BM"
    //位图文件大小
    const DWORD dibSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paletteSize + bmBitsSize;
    bmpFileHeader.bfSize = dibSize;
    bmpFileHeader.bfReserved1 = 0;
    bmpFileHeader.bfReserved2 = 0;
    bmpFileHeader.bfOffBits = static_cast<DWORD>(sizeof(BITMAPFILEHEADER))
        + static_cast<DWORD>(sizeof(BITMAPINFOHEADER)) + paletteSize;

    //6. 为位图内容分配内存 
    const auto dib = GlobalAlloc(GHND, bmBitsSize + paletteSize + sizeof(BITMAPINFOHEADER)); //内存句柄
    const auto lpBmpInfoHeader = static_cast<LPBITMAPINFOHEADER>(GlobalLock(dib)); //指向位图信息头结构
    *lpBmpInfoHeader = bmpInfoHeader;

    //7. 处理调色板
    ProcessPalette(hBitmap, bitmap, paletteSize, lpBmpInfoHeader);

    //8. 写入文件
    DWORD written = 0; //写入文件字节数   
    WriteFile(file, reinterpret_cast<LPSTR>(&bmpFileHeader), sizeof(BITMAPFILEHEADER),
        &written, nullptr); //写入位图文件头
    WriteFile(file, reinterpret_cast<LPSTR>(lpBmpInfoHeader), dibSize,
        &written, nullptr); //写入位图文件其余内容

    //9. 清理资源
    GlobalUnlock(dib);
    GlobalFree(dib);
    CloseHandle(file);

    return true;
}

//计算位图文件每个像素所占字节数
WORD GetBitmapBitCount()
{
    const auto dc = ::CreateDCA("DISPLAY", nullptr, nullptr, nullptr);
    //当前分辨率下每像素所占字节数
    const auto bits = ::GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES);
    ::DeleteDC(dc);

    //位图中每像素所占字节数
    WORD bitCount;
    if (bits <= 1)
        bitCount = 1;
    else if (bits <= 4)
        bitCount = 4;
    else if (bits <= 8)
        bitCount = 8;
    else
        bitCount = 24;

    return bitCount;
}

//处理调色板
void ProcessPalette(HBITMAP hBitmap, const BITMAP& bitmap,
    DWORD paletteSize, LPBITMAPINFOHEADER lpBmpInfoHeader)
{
    HANDLE oldPalette = nullptr;
    HDC dc = nullptr;
    const auto palette = GetStockObject(DEFAULT_PALETTE);
    if (palette != nullptr)
    {
        dc = ::GetDC(nullptr);
        oldPalette = ::SelectPalette(dc, static_cast<HPALETTE>(palette), FALSE);
        ::RealizePalette(dc); //实现设备调色板
    }

    //获取该调色板下新的像素值
    GetDIBits(dc, hBitmap, 0, static_cast<UINT>(bitmap.bmHeight),
        reinterpret_cast<LPSTR>(lpBmpInfoHeader) + sizeof(BITMAPINFOHEADER) + paletteSize,
        reinterpret_cast<BITMAPINFO*>(lpBmpInfoHeader), DIB_RGB_COLORS);

    //恢复调色板
    if (oldPalette != nullptr)
    {
        ::SelectPalette(dc, static_cast<HPALETTE>(oldPalette), TRUE);
        ::RealizePalette(dc);
        ::ReleaseDC(nullptr, dc);
    }
}